package simpledb.storage;

import simpledb.common.Type;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

/**
 * TupleDesc 主要定义了 Tuple 结构，这里是一个 TDItem 类型的数组
 * 一个 TDItem 对象包括 fieldType 和 fileName 两个属性，通过这两个属性来描述数据库的行
 * TupleDesc 定义数据库表的元数据，每个元数据既有类型也有名字 --- fileType 和 fileName
 * 这个类的主要功能包括：
 * 1、定义每个字段的类型（如整型、字符串等）；
 * 2、可选地定义字段名称；
 * 3、提供字段数量、字段类型查询接口；
 * 4、支持合并多个 TupleDesc；
 * 5、可以计算一个元组实际占用的字节数；
 * 6、可用于比较两个 TupleDesc 是否一致；
 * 7、可被序列化，便于网络传输或持久化。
 */
public class TupleDesc implements Serializable {

    // 存储每个字段的描述信息 TDItem
    private List<TDItem> descList;
    // 字段数量
    private int          fieldNum;

    /**
     * TDItem 是 TupleDesc 的内部类，用来封装单个字段的信息
     * 字段类型 Type
     * 字段名称 String
     */
    public static class TDItem implements Serializable {

        private static final long serialVersionUID = 1L;

        // 字段类型 INT_TYPE 或 STRING_TYPE
        public final Type         fieldType;

        // 字段名称 （可以为 null，表示匿名字段）
        public String             fieldName;

        // 构造函数：创建一个包含类型和名称的字段描述项
        public TDItem(Type fieldType, String fieldName) {
            this.fieldType = fieldType;
            this.fieldName = fieldName;
        }

        // 判断两个 TDItem 是否相等（只比较类型，不比较字段名）
        @Override
        public boolean equals(Object o) {
            if (o == null) {
                return false;
            }
            if (o == this) {
                return true;
            }
            if (!(o instanceof TDItem)) {
                return false;
            }
            TDItem other = (TDItem) o;
            return other.fieldType == this.fieldType;
        }

        // 返回字段描述的字符串形式，例如 "name(STRING_TYPE)"
        public String toString() {
            return fieldName + "(" + fieldType + ")";
        }
    }

    // 序列化版本号
    private static final long serialVersionUID = 1L;

    /**
     * 获取一个迭代器，可以遍历所有字段描述项（TDItem）
     *
     * @return Iterator<TDItem>
     */
    public Iterator<TDItem> iterator() {
        return this.descList.iterator();
    }

    /**
     * 构造函数：根据字段类型数组和字段名数组构造一个新的 TupleDesc。
     *
     * @param typeAr 字段类型数组（至少一个元素）
     * @param fieldAr 字段名数组（可以为 null）
     */
    public TupleDesc(Type[] typeAr, String[] fieldAr) {
        if (typeAr.length != fieldAr.length) {
            throw new IllegalArgumentException("类型数组与字段名数组长度必须一致");
        }
        this.descList = new ArrayList<>(typeAr.length);
        this.fieldNum = typeAr.length;
        for (int i = 0; i < typeAr.length; i++) {
            final TDItem item = new TDItem(typeAr[i], fieldAr[i]);
            this.descList.add(item);
        }
    }

    /**
     * 构造函数重载：仅传入字段类型数组，默认字段名为 null（匿名字段）
     *
     * @param typeAr 字段类型数组
     */
    public TupleDesc(Type[] typeAr) {
        this(typeAr, new String[typeAr.length]);
    }

    /**
     * 构造函数：从一个 TDItem 列表构造 TupleDesc
     *
     * @param itemList TDItem 列表
     */
    public TupleDesc(final List<TDItem> itemList) {
        this.descList = new ArrayList<>(itemList);
        this.fieldNum = this.descList.size();
    }

    /**
     * 获取当前 TupleDesc 中字段的数量
     *
     * @return 字段数量
     */
    public int numFields() {
        return this.fieldNum;
    }

    /**
     * 获取第 i 个字段的名称
     *
     * @param i 字段索引
     * @return 字段名（可能为 null）
     * @throws NoSuchElementException 如果索引无效
     */
    public String getFieldName(int i) throws NoSuchElementException {
        if (i >= this.fieldNum || i < 0) {
            throw new NoSuchElementException("字段索引超出范围");
        }
        return this.descList.get(i).fieldName;
    }

    /**
     * 获取第 i 个字段的类型
     *
     * @param i 字段索引
     * @return 字段类型
     * @throws NoSuchElementException 如果索引无效
     */
    public Type getFieldType(int i) throws NoSuchElementException {
        if (i >= this.fieldNum) {
            throw new NoSuchElementException("字段索引超出范围");
        }
        return this.descList.get(i).fieldType;
    }

    /**
     * 获取 TDItem 列表（字段描述列表）
     *
     * @return TDItem 列表
     */
    public List<TDItem> getDescList() {
        return descList;
    }

    /**
     * 根据字段名查找对应的字段索引
     *
     * @param name 字段名
     * @return 字段索引
     * @throws NoSuchElementException 如果未找到对应字段名
     */
    public int fieldNameToIndex(String name) throws NoSuchElementException {
        if (name == null) {
            throw new NoSuchElementException("字段名为 null");
        }
        for (int i = 0; i < this.fieldNum; i++) {
            if (name.equals(this.descList.get(i).fieldName)) {
                return i;
            }
        }
        throw new NoSuchElementException("找不到该字段名");
    }

    /**
     * 计算一个元组所占的总字节数（固定大小）
     *
     * @return 总字节数
     */
    public int getSize() {
        int size = 0;
        for (int i = 0; i < this.fieldNum; i++) {
            size += this.descList.get(i).fieldType.getLen();
        }
        return size;
    }

    /**
     * 合并两个 TupleDesc 对象，返回一个新的 TupleDesc，
     * 其字段是 td1 和 td2 的字段拼接而成。
     *
     * @param td1 第一个 TupleDesc
     * @param td2 第二个 TupleDesc
     * @return 合并后的新 TupleDesc
     */
    public static TupleDesc merge(TupleDesc td1, TupleDesc td2) {
        final List<TDItem> newDesc = new ArrayList<>(td1.descList);
        newDesc.addAll(td2.descList);
        return new TupleDesc(newDesc);
    }

    /**
     * 判断两个 TupleDesc 是否相等（字段数、类型一一对应）
     *
     * @param o 要比较的对象
     * @return 是否相等
     */
    public boolean equals(Object o) {
        if (o == null)
            return false;
        if (o == this)
            return true;
        if (!(o instanceof TupleDesc))
            return false;

        TupleDesc tdo = (TupleDesc) o;
        if (this.numFields() != tdo.numFields())
            return false;

        Iterator<TDItem> it1 = tdo.iterator();
        Iterator<TDItem> it2 = this.iterator();
        while (it2.hasNext()) {
            if (!it1.next().equals(it2.next()))
                return false;
        }
        return true;
    }

    /**
     * 哈希方法（未实现）
     * 如果要用 TupleDesc 作为 HashMap 的 key，需要实现此方法。
     */
    public int hashCode() {
        throw new UnsupportedOperationException("unimplemented");
    }

    /**
     * 返回 TupleDesc 的字符串表示，用于调试输出。
     * 示例格式："descList=[name(STRING_TYPE), age(INT_TYPE)]"
     */
    @Override
    public String toString() {
        return "TupleDesc{" + "descList=" + descList + ", fieldNum=" + fieldNum + '}';
    }
}
